#coding=utf-8

"""
Superescalares -- múltiplos pipelines

TODO
* detectar limitações:
    dependência de dados verdadeira: usar um reg antes de ele ser escrito em uma instrução anterior
    dependência de saída: duas instruções escrevendo no mesmo registrador ao mesmo tempo
    antidependência: um registrador sendo usado enquanto está sendo escrito em instrução posterior
* tratar limitações pulando instruções
/TODO
* Usar despacho e terminação fora de ordem, para facilitar e aumentar velocidade


Sequência
------ recebe objeto parser para facilitar acesso ao código e dependÊncias

detectar dependências
 código pronto, dentro do parser

tratar dependências?
 criar código ou só adiar execução?

executar código
 estágio 0: decodificar a instrução -- determinar tempo de ocupaￃﾧￃﾣo de cada uma
 estágio 1: ler registradores da memória
 estágio 2: executar os cálculos
 estágio 3: escrever/ler os resultados na/da memória
 estágio 4: escrever os resultados no registrador

 primeiro executa estágios 0 e 1 para N instruções // N = 2 ?
  instruções guardadas numa estrutura temporária, com N posições
 depois, passa essas instruções para uma janela de execução
  instruções passadas para outra estrutura, (com lugares infinitos livres)?
 quando houver pipelines livres, enviar para um deles a instrução para execução
  pipelines serão o quê? definir
 após a execução, enviar para saída: escrita na memória
  simplesmente tira elas e manda pra uma estrutura que indica que acabou, ou coisa assim

 pensar em como aplicar modo de exibição

"""


"""
CONSTANTES
"""

AMT_PIPES = 3;         #quantidade de pipelines
AMT_SAMPLE = 5;        #quantidade de instruções passadas para a janela de 
                       # teste de dependências     

import AsmParser

"""
    Cada um dos pipelines
"""
class Pipeline():
    __instr = None;
    __free = True;
    
    def __init__(self):
        self.__instr = None;
        self.__free = True;
        return None;
    
    # Ocupa o pipeline com uma instrução    
    def setInstruction(self, inst):
        self.__free = False;
        self.__instr = inst;
    
    #efetivamente executa o pipeline    
    def goPipeline(self):
        self.__instr.advPipeStage();
        self.__instr.advCicle();
        lbl = self.__instr.getLabel()
        lbl = lbl + " " + str(self.__instr.getStage());
        #Liberar o pipeline após terminar a execução
        if self.getStage() > 3:
            self.__init__();
        return lbl;
        
    def isFree(self):
        return self.__free;
    
    def getStage(self):
        return self.__instr.getStage();
    
#-----------------------------------------------------------------------------------#

class SuperscalarEngine():
    __parser = None;       #Isso vai ser o objeto Parsed -- permite aproveitar detecção de dependências          
    __code = {};           #Código rearmazenado localmente 
    __window = [];         #janela de execução
    __testWin = [];        #janela de testes
    __pipeline = [];       #organizador de pipelines 
    
    """
       p é um objeto parser
    """
    def __init__(self, p):
        self.__parser = p;
        self.__code = p.getParsedInstr();
    
    """levar um conjunto de N instruções para uma janela de testes
         dentro dessa janela, conferir se há dependência dos 3 tipos
         caso haja, somar em um contador
         a instrução que tiver menos detalhes, é adicionada.
       
       retorna um objeto Instruction
    """
    def checkDeps(self):
        ret = None;
        insts = [];
        
        #para cada instrução dentro da janela
        for inst in self.__testWin:
            #quantidade de dependências encontradas para a instrução atual
            score = 0;
            #pega o primeiro registrador dela 
            #  (presume que os anteriores já foram testados pela função)
            testReg = inst[1].getOperandos()[0].getRegister();
            #testa todas as outras instruções
            for comp in self.__testWin:
                if comp != inst:
                    #testa cada registrador da instrução
                    for reg in comp[1].getOperandos():
                        if reg.getRegister() == testReg:
                            #não faz distinção das instruções
                            score = score + 1;
            insts.append((score, inst))

        #procura a que tem o menor score
        ret = insts[0]
        for tup in insts:
            if ret[0] < tup[0]:
                ret = tup;
        self.__testWin.remove(ret[1]);
        return ret[1][1];
        
    """
     passa instruções para uma janela de execução
       instruções passadas para outra estrutura
     quando houver pipelines livres, enviar para um deles uma instrução tirada da janela
       pipelines são classes que seguram uma instrução e executam a passagem de estágios e ciclos
     após a execução, enviar para saída: escrita na memória
       limpa a classe Pipeline correspondente, e puxa outra instrução, ou segura em branco, esperando 
    """    
    def doExecute(self):        
        #inicializa a quantidade de pipelines
        displays = [];
        for i in range(AMT_PIPES):
            self.__pipeline.append(Pipeline());
            displays.append("pipe " + str(i) +" -- ");
        
        for i in range(AMT_SAMPLE):
            if self.__code:
                self.__testWin.append(self.__code.popitem())

        #percorre o código -- execução per se
        while True:

            #tira uma instrução da janela de testes
            # e coloca na janela de fato
            if self.__testWin:
                self.__window.append(self.checkDeps())
            #coloca mais uma instrução na janela de testes, para a próxima
            # iteração
            if self.__code:
                self.__testWin.append(self.__code.popitem())

            #passa uma instrução da janela para o pipeline
            # se não houver pipes livres, simplesmente sai, a janela permanece intacta

            #procura um pipe livre
            if self.__window:
                for i in range(AMT_PIPES):
                    if self.__pipeline[i].isFree() and self.__window:
                        #insere a instrução nele #ui
                        self.__pipeline[i].setInstruction(self.__window[0]);
                        self.__window.remove(self.__window[0]);

            #executa os pipelines
            for i in range(AMT_PIPES):
                if not self.__pipeline[i].isFree():
                    displays[i] = displays[i] + "[ " + self.__pipeline[i].goPipeline() + " ]"

            #testa se todos os pipelines ainda estão em uso
            if not self.__window:
                countdown = 0
                for i in range(AMT_PIPES):
                    if self.__pipeline[i].isFree():
                        countdown = countdown + 1;
                if countdown == AMT_PIPES:
                    break;
        
        for i in range(AMT_PIPES):
            print displays[i]
                
def main():
    p = AsmParser.AsmParser();
    p.openASMFile("fonte.txt");
    p.doParse();
    ss = SuperscalarEngine(p);
    ss.doExecute();
main();
